// Program to compute match results, update player hcps, and produce the weekly report

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>

#include "golf.h"
#include "write_master.h"
#include "util.h"
#include "compute_hcp.h"

// globals
teamsInfo_t teamsInfo[MAX_TEAMS];
course_t courseSettings;        // holds par and hole ranks for scoring the round
individualMatch_t matchPlayers[NO_PLAYERS_PER_MATCH];
teamMatchHeader_t teamMatchHeader;
matchFileHeaderRecord_t matchFileHeader;
player_t players[MAX_PLAYERS + 1];  /* use the players id's as index.  The
                                       zero element of this vector is ignored */

void readMasterFile(char *fileName, int &nt, int &np)
{
    int i, j;
    FILE *fp;
    int idum;
    char cdum;
    float fdum;

    /* open the master data file */
    if ((fp = fopen(fileName, "r")) == NULL) {
        perror("Error opening Master file");
        exit(-1);
    }

    /* read the number of teams and the number of players making up the league  */
    if ((fscanf(fp, " %2d %2d \n", &nt, &np)) == -1) {
        printf("Error Master File - Writing header \n");
        exit(-1);
    }

    /* read all player records.  Input is ASCII  */
    // TODO: make sure the [0] does the right thing
    for (i = FIRST_PLAYER; i <= np; i++) {
        if ((fscanf(fp, "%2d %s %1c %f", &(players[i].id_number),
                    &(players[i].name[0]),
                    &(players[i].initial), &(players[i].curr_hcp))) == -1) {
            printf
                ("Error Master File - Writing player record number %d - id,name,and initial",
                 i);
            exit(-1);
        }

        /* now read the players last six scores  */
        for (j = 0; j < HCP_WINDOW_LEN; j++)
            if ((fscanf(fp, "%f ", &fdum)) == -1) {
                printf
                    ("Error Master File - Reading player record %d previous scorce %d \n",
                     i, j);
                exit(-1);
            } else
                players[i].ESCAdjustedScores[j] = fdum;

        /* read the sub history for each round played */
        for (j = 0; j < MAX_RNDS; j++)
            if ((fscanf(fp, "%2d ", &idum)) == -1) {
                printf
                    ("Error Master File - Reading record %d sub History %d \n",
                     i, j);
                exit(-1);
            } else
                players[i].subNumber[j] = idum;

        /* read the scores for each round played */
        for (j = 0; j < MAX_RNDS; j++)
            if ((fscanf(fp, "%2d ", &idum)) == -1) {
                printf
                    ("Error Master File - Reading record %d scores %d \n",
                     i, j);
                exit(-1);
            } else
                players[i].all_scores[j] = idum;

        /* read the net scores for each round played */
        for (j = 0; j < MAX_RNDS; j++)
            if ((fscanf(fp, "%2d ", &idum)) == -1) {
                printf
                    ("Error Master File - Reading record %d net scores %d \n",
                     i, j);
                exit(-1);
            } else
                players[i].netScores[j] = idum;

        /* read the putts for each round played */
        for (j = 0; j < MAX_RNDS; j++)
            if ((fscanf(fp, "%2d ", &idum)) == -1) {
                printf
                    ("Error Master File - Reading record %d putts %d \n", i, j);
                exit(-1);
            } else
                players[i].putts[j] = idum;

        /* read the points the player has won each match  */
        for (j = 0; j < MAX_RNDS; j++)
            if ((fscanf(fp, "%2d ", &idum)) == -1) {
                printf
                    ("Error Master File - Reading record %d points won %d \n",
                     i, j);
                exit(-1);
            } else
                players[i].matchPointsWon[j] = idum;

        /* read the history of which nine holes each match was played on   */
        for (j = 0; j < MAX_RNDS; j++)
            if ((fscanf(fp, "%1c ", &cdum)) == -1) {
                printf
                    ("Error Master File - Reading record %d which nine %d \n",
                     i, j);
                exit(-1);
            } else
                players[i].whichNinePlayed[j] = cdum;

        /* read the sub history for this player  */
        for (j = 0; j < MAX_RNDS; j++)
            if ((fscanf(fp, "%2d ", &idum)) == -1) {
                printf
                    ("Error Master File - Reading record %d sub number %d \n",
                     i, j);
                exit(-1);
            } else
                players[i].numHolesPlayed[j] = idum;

    }                           /* end of loop over all player records */

    return;
}                               /* end of function read_master_file */

void fillInParScore(char whichNine, int index)
{
    int i;

    if (whichNine == FRONT)
        for (i = 0; i < 9; i++)
            matchPlayers[index].scores[i] = frontPar[i];
    else if (whichNine == BACK)
        for (i = 0; i < 9; i++)
            matchPlayers[index].scores[i] = backPar[i];
    else {
        printf("Error:fillInParScore-Unknow which nine indicator \n");
        exit(-1);
    }

    for (i = 0; i < 9; i++)
        matchPlayers[index].putts[i] = 2;   // par always two putts

    return;
}                               // end of function fillInParScore  

void readPlayerScore(FILE * fp, int index)
{
    int i;
    int num;
    int items;

    for (i = 0; i < teamMatchHeader.nholesPlayed; i++)
        if ((items = fscanf(fp, "%d", &num)) != 1) {
            printf
                ("Error:readPlayerScore-reading player's %d score for hole %d\n",
                 matchPlayers[index].id, i + 1);
            exit(-1);
        } else
            matchPlayers[index].scores[i] = num;

    for (i = 0; i < teamMatchHeader.nholesPlayed; i++)
        if ((items = fscanf(fp, "%d", &num)) != 1) {
            printf
                ("Error:readPlayerScore-reading players %d putts for hole %d\n",
                 matchPlayers[index].id, i + 1);
            exit(-1);
        } else
            matchPlayers[index].putts[i] = num;

    return;

}                               // end of function readPlayerScore

void readMatchHeader(FILE * fp)
{
    int m, d, y, match, num_of_matches_played;

    if ((fscanf
         (fp, "%d %d %d %d %d", &match, &m, &d, &y,
          &num_of_matches_played)) == -1) {
        printf("Error:readMatchHeader-reading match file header \n");
        exit(-1);
    } else {                    /* fill in match date fields in the global date structure */

        matchFileHeader.month = m;
        matchFileHeader.day = d;
        matchFileHeader.year = y;
        matchFileHeader.matchNumber = match;
        matchFileHeader.numMatchesPlayed = num_of_matches_played;
    }

    return;
}                               // end of readMatchHeader  

void initMatchStructure(void)
{
    int i, j;

    for (i = 0; i < 4; i++) {
        matchPlayers[i].id = -1;
        matchPlayers[i].subFor = -1;
        matchPlayers[i].forfiet = false;
        matchPlayers[i].hcp = 0.0;
        matchPlayers[i].totalScore = 0;
        matchPlayers[i].totalPts = 0;
        matchPlayers[i].netScore = 0;

        for (j = 0; j < 9; j++) {
            matchPlayers[i].scores[j] = NO_SCORE;
            matchPlayers[i].putts[j] = NO_PUTTS;
            matchPlayers[i].pts[j] = 0;
            matchPlayers[i].adjScores[j] = 0;
            matchPlayers[i].hcpStrokes[j] = 0;
        }
    }

    return;
}

int teamMember(int teamNumber, int playerID)
{
    if (playerID == (teamNumber * 2 - 1) || playerID == (teamNumber * 2))
        return (true);
    else
        return (false);

}                               // end of function teamMember

int notValidSub(int id, int nteams, int totalPlayers)
{
    // to be valid a sub number must be = to the global PAR_AS_SUB value
    // or to be in the range of (nteam*2) < id <= totalPlayers
    // return true if the id is not a valid sub number else return false
    if (id == PAR_AS_SUB)
        return (false);
    else if (id <= (nteams * 2))    // this is a team member - he can't sub
        return (true);
    else if (id > totalPlayers)
        return (true);
    else
        return (false);
}                               // end of function notValidSub 

void readTeamMatch(FILE * fp, int teamMatchNumber, int nteams, int totalPlayers)
{
    int j;
    int t1, t2, id, sub;
    char wn;
    int nholesPlayed;

    /* read a team match header */
    if ((fscanf(fp, "%d %d %c %d", &t1, &t2, &wn, &nholesPlayed)) == -1) {
        printf
            ("Error:readTeamMatch-reading header for team match %d \n",
             teamMatchNumber);
        exit(-1);
    } else                      // fill in the team match header   
    {
        teamMatchHeader.team1 = t1;
        teamMatchHeader.team2 = t2;
        teamMatchHeader.whichNine = wn;
        teamMatchHeader.nholesPlayed = nholesPlayed;
    }

    /* read in the scores and putts for the players that participated in this 
       team match. */

    /* first read player information for the first team */
    for (j = 0; j < 2; j++) {
        if ((fscanf(fp, "%d %d", &id, &sub)) == -1) {
            printf
                ("Error:readTeamMatch-reading player %2d of team %2d of foursome %2d\n",
                 (j + 1), teamMatchHeader.team1, teamMatchNumber);
            exit(-1);
        } else {
            matchPlayers[j].id = id;
            matchPlayers[j].subFor = sub;
        }

        // error check  Make sure this player, or the players he's subbing for is a member 
        // of the team 
        if (!teamMember(t1, id) && !teamMember(t1, sub)) {
            printf
                ("Player ID is not valid for this team %2d  Player id is %2d  Sub number is %2d \n",
                 t1, id, sub);
            exit(-1);
        }
        // if the player is a sub make sure the sub number is valid
        if (!teamMember(t1, id)
            && notValidSub(id, nteams, totalPlayers)) {
            printf
                ("Not a valid sub number  team  %2d  player id %2d \n", t1, id);
            exit(-1);
        }
        // if par played then the player must have forfeited the round
        if (id == PAR_AS_SUB) {
            matchPlayers[j].forfiet = true; // remember the forfeiit 
            fillInParScore(wn, j);  // fill in par as the score
        } else
            readPlayerScore(fp, j);

    }                           // end read team1 loop

    /* read the player information for the second team */
    for (j = 2; j < 4; j++) {
        if ((fscanf(fp, "%d %d", &id, &sub)) == -1) {
            printf
                ("Error:readTeamMatch-reading player %2d of team %2d of foursome %2d\n",
                 (j + 1), teamMatchHeader.team2, teamMatchNumber);
            exit(-1);
        } else {
            matchPlayers[j].id = id;
            matchPlayers[j].subFor = sub;
        }

        // error check  Make sure this player, or the players he's subbing for is a member 
        // of the team 
        if (!teamMember(t2, id) && !teamMember(t2, sub)) {
            printf
                ("Player ID is not valid for this team %2d  Player id is %2d  Sub number is %2d \n",
                 t2, id, sub);
            exit(-1);
        }
        // if the player is a sub make sure the sub number is valid
        if (!teamMember(t2, id)
            && notValidSub(id, nteams, totalPlayers)) {
            printf
                ("Not a valid sub number  team  %2d  player id %2d \n", t2, id);
            exit(-1);
        }

        if (matchPlayers[j].id == PAR_AS_SUB) {
            matchPlayers[j].forfiet = true;
            fillInParScore(wn, j);
        } else
            readPlayerScore(fp, j);

    }                           // end read team2 loop

    return;
}                               // end of function readTeamMatch

void teamABPlayers(int &firstPlayer, int &secondPlayer)
{
    int temp;
    int a, b, n;

    // determine the A and B player for a team - return the index into the matchPlayer structure

    // first compare the hcps - the player with the lowest hcp is the A player
    if (matchPlayers[firstPlayer].hcp < matchPlayers[secondPlayer].hcp)
        return;                 // no change needed
    else if (matchPlayers[firstPlayer].hcp > matchPlayers[secondPlayer].hcp) {  // second player has lowest hcp - swap the indexes
        temp = firstPlayer;
        firstPlayer = secondPlayer;
        secondPlayer = temp;
        return;
    }
    // if the team mates have the same hcp the alphabetaical order of there last names determines
    // the A and B players.  If the names are the same the alphabetical order their first initials
    // determine the A and B players.  If even the initials are the same the indexes are returned
    // unchanged.

    a = matchPlayers[firstPlayer].id;   // save some typing
    b = matchPlayers[secondPlayer].id;
    n = strcmp(players[a].name, players[b].name);

    if (n < 0)                  // names are already in alphabetaical order
        return;
    else if (n > 0)             // swap the indexes
    {                           // second player's name first
        temp = firstPlayer;
        firstPlayer = secondPlayer;
        secondPlayer = temp;
        return;
    }
    // if we get here only the first initials are left
    if (players[a].initial > players[b].initial)    // swap the indexes
    {                           // second player's first initial comes before the first player's
        temp = firstPlayer;
        firstPlayer = secondPlayer;
        secondPlayer = temp;
        return;
    }
    // if we fall through the checks above the indexes are either already in order
    // or the initials are the same and we have no other information to use.  For both
    // cases the indexes are returned unchanged.

    return;
}                               // end of function  teamABPlayers

void setABPlayers(int &t1A, int &t1B, int &t2A, int &t2B)
{
    int a, b;

    // place holder until I have time to automate this function.
    // For now it is necessary to order the input data

    /* t1A = 0;
       t1B = 1;
       t2A = 2;
       t2B = 3;
     */

    // process the first team - indexs 0 & 1 into the matchplayers array
    a = 0;
    b = 1;
    teamABPlayers(a, b);
    t1A = a;
    t1B = b;
    // process the second team - indexs 2 & 3 into the matchplayers array
    a = 2;
    b = 3;
    teamABPlayers(a, b);
    t2A = a;
    t2B = b;

}                               // end of function setABPlayers

void setCourseSettings(char whichnine)
{
    int i;

    if (whichnine == 'f')       // front nine
        for (i = 0; i < 9; i++) {
            courseSettings.holePar[i] = frontPar[i];
            courseSettings.holeRank[i] = frontRank[i];
    } else if (whichnine == 'b')    // back nine
        for (i = 0; i < 9; i++) {
            courseSettings.holePar[i] = backPar[i];
            courseSettings.holeRank[i] = backRank[i];
    } else                      // error
    {
        printf
            ("Error:setCourseSettings-Invalid nine hole indicator %c\n",
             whichnine);
        exit(-1);
    }

    return;
}                               // end of function setCourseSettings

float establish_first_hcp(int index)
{
    int i, score;

    score = 0;

    for (i = 0; i < teamMatchHeader.nholesPlayed; i++)
        score += matchPlayers[index].scores[i];

    return ((float)score - TOTAL_PAR);

}

int totalScore(int index)
{
    int i;
    int sum = 0;
    float tempESCscore = 0.0;
    int id;
    float hcp, maxStrokes;

    id = matchPlayers[index].id;

    // hcp = players[id].curr_hcp;
    hcp = matchPlayers[index].hcp;

    if (hcp <= 9.4)
        maxStrokes = 2.0;
    else if (hcp <= 18.4)
        maxStrokes = 3.0;
    else
        maxStrokes = 4.0;

    // just loop over the holes played, which may be less then 9
    for (i = 0; i < teamMatchHeader.nholesPlayed; i++) {
        sum = sum + matchPlayers[index].scores[i];

        // also find the player's ESC score while were here.  This will be used later to compute the 
        // player's new hcp.  Doing it here because the hole-by-hole par(based on which nine was played
        // has been set.  And because the hole-by-hole scores are not available when the new hcps are 
        // computed.
        if (matchPlayers[index].scores[i] >
            (courseSettings.holePar[i] + maxStrokes))
            tempESCscore += (float)courseSettings.holePar[i] + maxStrokes;
        else
            tempESCscore += (float)matchPlayers[index].scores[i];
    }

    players[id].ESCscore = tempESCscore;    // use this value for new hcp calculation

    return (sum);

}                               // end of function totalScores

void getPlayerHCPs()
{
    int i;
    int id;
    float hcp;

    for (i = 0; i < 4; i++) {

        id = matchPlayers[i].id;
        // if par is playing then get the hcp of the "real" team member
        // This is needed to determine the A & B players later before
        // par's score is subsituted for match points computations.
        // This ensures that par does not automaticly become a team's A player
        if (id == PAR_AS_SUB)
            id = matchPlayers[i].subFor;

        hcp = players[id].curr_hcp;

        // if it's a new player then use his score minus par as his hcp
        if (hcp == NO_HCP)
            hcp = establish_first_hcp(i);

        matchPlayers[i].hcp = hcp;

    }                           // end for loop

    return;
}                               // end of function get PlayerHCPs 

void getTotalScores()
{
    int i;

    for (i = 0; i < 4; i++) {
        matchPlayers[i].totalScore = totalScore(i);

    }

    return;
}                               // end of function getTotalScores

void assignHcpStrokes(int playerIndex, int nStrokes)
{
    int i, n, index;

    for (i = 0; i < nStrokes; i++) {
        n = i;
        index = courseSettings.holeRank[(n %= 9)];  // not very efficent 
        index -= 1;             // hole ranks range from 1 to 9, the vector is 0 to 8
        matchPlayers[playerIndex].hcpStrokes[index] += 1;
    }

    return;
}                               // end of function assignHcpStrokes

void setHoleByHoleHcpStrokes(int player1, int player2)
{
    int hcpDiff;
    float x;

    x = (float)fabs(matchPlayers[player1].hcp - matchPlayers[player2].hcp);
    hcpDiff = (int)(x + 0.5);   // round up 

    if (matchPlayers[player1].hcp < matchPlayers[player2].hcp)
        assignHcpStrokes(player2, hcpDiff);
    else if (matchPlayers[player1].hcp > matchPlayers[player2].hcp)
        assignHcpStrokes(player1, hcpDiff);

    // take no action if the hcps are equal

    return;
}                               // end of function setHoleByHoleHcpStrokes

void computeNetHoleScores(void)
{
    int i, j;

    for (i = 0; i < 4; i++)
        for (j = 0; j < teamMatchHeader.nholesPlayed; j++)
            matchPlayers[i].adjScores[j] =
                matchPlayers[i].scores[j] - matchPlayers[i].hcpStrokes[j];

    return;
}

void scoreMatch(int player1, int player2)
{
    int i;
    int score1, score2;

    ;

    // determine hole by hole points for each player

    for (i = 0; i < teamMatchHeader.nholesPlayed; i++) {
        score1 = matchPlayers[player1].adjScores[i];
        score2 = matchPlayers[player2].adjScores[i];

        // check for a 10x rule flag in the "putts" vector. If its there set the score for the player big enough
        // to insure he loses the hole, unless both players pickedup
        if (matchPlayers[player1].putts[i] == TENX_FLAG)
            score1 = (int)BIGNUM;
        if (matchPlayers[player2].putts[i] == TENX_FLAG)
            score2 = (int)BIGNUM;

        if (score1 < score2) {
            matchPlayers[player1].pts[i] = WIN;
            matchPlayers[player2].pts[i] = LOSE;
        } else if (score1 > score2) {
            matchPlayers[player1].pts[i] = LOSE;
            matchPlayers[player2].pts[i] = WIN;
        } else                  // tie
        {
            matchPlayers[player1].pts[i] = TIE;
            matchPlayers[player2].pts[i] = TIE;
        }

    }                           // end for loop

    // split the points for holes not played
    if (teamMatchHeader.nholesPlayed < 9)
        for (i = teamMatchHeader.nholesPlayed; i < 9; i++) {
            matchPlayers[player1].pts[i] = TIE;
            matchPlayers[player2].pts[i] = TIE;
        }

    return;
}                               // end of function scoreMatch

void totalThePointsWon(int t1A, int t2A, int t1B, int t2B)
{
    int i, j;
    int subFor;
    // modified May 2003 to give points to a player that forfeits
    // index 0 plays index 2
    // index 1 plays index 3
    for (i = 0; i < 4; i++) {
        for (j = 0; j < 9; j++)
            matchPlayers[i].totalPts += matchPlayers[i].pts[j];
    }

    for (i = 0; i < 4; i++)     // includes holes not played for which the pts were split
    {
        //for (j = 0; j < 9; j++) matchPlayers[i].totalPts += matchPlayers[i].pts[j];

        if (matchPlayers[i].forfiet) {
            subFor = matchPlayers[i].subFor;

            if (i == t1A) {
                if (matchPlayers[t2A].forfiet)
                    matchPlayers[i].totalPts = 6;
                else {
                    matchPlayers[i].totalPts =
                        (int)MAX_INDIVIDUAL_PTS -
                        matchPlayers[t2A].totalPts - 3;
                    matchPlayers[t2A].totalPts += 3;
                    if (matchPlayers[i].totalPts > 9)
                        matchPlayers[i].totalPts = 9;
                    //if(matchPlayers[i].totalPts < 0 ) matchPlayers[i].totalPts = 0;
                }
            } else if (i == t2A) {
                if (matchPlayers[t1A].forfiet)
                    matchPlayers[i].totalPts = 6;
                else {
                    matchPlayers[i].totalPts =
                        (int)MAX_INDIVIDUAL_PTS -
                        matchPlayers[t1A].totalPts - 3;
                    matchPlayers[t1A].totalPts += 3;
                    if (matchPlayers[i].totalPts > 9)
                        matchPlayers[i].totalPts = 9;
                    //if(matchPlayers[i].totalPts < 0 ) matchPlayers[i].totalPts = 0;
                }
            } else if (i == t1B) {
                if (matchPlayers[t2B].forfiet)
                    matchPlayers[i].totalPts = 6;
                else {
                    matchPlayers[i].totalPts =
                        (int)MAX_INDIVIDUAL_PTS -
                        matchPlayers[t2B].totalPts - 3;
                    matchPlayers[t2B].totalPts += 3;
                    if (matchPlayers[i].totalPts > 9)
                        matchPlayers[i].totalPts = 9;
                    //if(matchPlayers[i].totalPts < 0 ) matchPlayers[i].totalPts = 0;
                }
            } else if (i == t2B) {
                if (matchPlayers[t1B].forfiet)
                    matchPlayers[i].totalPts = 6;
                else {
                    matchPlayers[i].totalPts =
                        (int)MAX_INDIVIDUAL_PTS -
                        matchPlayers[t1B].totalPts - 3;
                    matchPlayers[t1B].totalPts += 3;
                    if (matchPlayers[i].totalPts > 9)
                        matchPlayers[i].totalPts = 9;
                    //if(matchPlayers[i].totalPts < 0 ) matchPlayers[i].totalPts = 0;
                }
            }

            if (matchPlayers[i].totalPts < 0)
                matchPlayers[i].totalPts = 0;

        }
    }
    return;
}                               // end of function totalThePointsWon

void totalNetScores(void)
{
    int i, j;

    for (i = 0; i < 4; i++)
        for (j = 0; j < teamMatchHeader.nholesPlayed; j++)
            matchPlayers[i].netScore += matchPlayers[i].adjScores[j];

    return;
}                               // end of function totalNet Scores

int rank(int holeNum)
{
    int i;

    for (i = 0; i < 9; i++)
        if (holeNum == courseSettings.holeRank[i])
            return (i + 1);

    return (0);                 // this should never happen
}                               // end of function rank

void writeDebugInfo(FILE * db, int t1A, int t1B, int t2A, int t2B)
{
    int i, j;
    int globalID;

    fprintf(db, "\n\n");
    fprintf(db, "Team 1      : %3d\n", teamMatchHeader.team1);
    fprintf(db, "Team 2      : %3d\n", teamMatchHeader.team2);
    fprintf(db, "Which Nine  : %c\n", teamMatchHeader.whichNine);
    fprintf(db, "nHolesPlayed: %3d\n", teamMatchHeader.nholesPlayed);

    globalID = matchPlayers[t1A].id;
    fprintf(db, "Team 1 A    : %s\n", players[globalID].name);
    globalID = matchPlayers[t1B].id;
    fprintf(db, "Team 1 B    : %s\n", players[globalID].name);
    globalID = matchPlayers[t2A].id;
    fprintf(db, "Team 2 A    : %s\n", players[globalID].name);
    globalID = matchPlayers[t2B].id;
    fprintf(db, "Team 2 B    : %s\n\n", players[globalID].name);

    fprintf(db, "Hole Num : ");
    for (i = 0; i < 9; i++)
        fprintf(db, "%3d", (i + 1));
    fprintf(db, "\n");

    fprintf(db, "Hole Par : ");
    for (i = 0; i < 9; i++)
        fprintf(db, "%3d", courseSettings.holePar[i]);
    fprintf(db, "\n");

    fprintf(db, "Hole Rank: ");
    for (i = 0; i < 9; i++)
        fprintf(db, "%3d", rank(i + 1));
    fprintf(db, "\n");

    for (i = 0; i < 4; i++) {
        globalID = matchPlayers[i].id;
        fprintf(db, "Player id: %3d\n", matchPlayers[i].id);
        fprintf(db, "Name     : %s\n", players[globalID].name);
        fprintf(db, "Sub For  : %3d\n", matchPlayers[i].subFor);
        fprintf(db, "hcp      : %4.1f\n", matchPlayers[i].hcp);
        fprintf(db, "forfiet  : %3d\n", matchPlayers[i].forfiet);
        fprintf(db, "tot score: %3d\n", matchPlayers[i].totalScore);
        fprintf(db, "Net score: %3d\n", matchPlayers[i].netScore);
        fprintf(db, "Tot Pts  : %3d\n", matchPlayers[i].totalPts);

        fprintf(db, "Scores   : ");
        for (j = 0; j < 9; j++)
            fprintf(db, "%3d", matchPlayers[i].scores[j]);
        fprintf(db, "\n");

        fprintf(db, "Hcp Stks : ");
        for (j = 0; j < 9; j++)
            fprintf(db, "%3d", matchPlayers[i].hcpStrokes[j]);
        fprintf(db, "\n");

        fprintf(db, "Adj Score: ");
        for (j = 0; j < 9; j++)
            fprintf(db, "%3d", matchPlayers[i].adjScores[j]);
        fprintf(db, "\n");

        fprintf(db, "Points   : ");
        for (j = 0; j < 9; j++)
            fprintf(db, "%3d", matchPlayers[i].pts[j]);
        fprintf(db, "\n");

        fprintf(db, "Putts    : ");
        for (j = 0; j < 9; j++)
            fprintf(db, "%3d", matchPlayers[i].putts[j]);
        fprintf(db, "\n");

    }                           // end for loop

    return;
}                               // end of function writeDebugInfo

int totalPutts(int index)
{
    int i;
    int sum = 0;
    int temp = 0;

    for (i = 0; i < teamMatchHeader.nholesPlayed; i++) {
        temp = matchPlayers[index].putts[i];
        if (temp == TENX_FLAG)
            sum += 3;           // 10x rule - 3 putts when picking up after 10 
        else
            sum += temp;
    }

    return (sum);
}                               // end of function totalPutts

void upDatePlayersMatchInfo()
{
    int i, id, rP;
    int m;

    m = matchFileHeader.matchNumber - 1;    // convert matchNumber to an index

    for (i = 0; i < 4; i++) {
        id = matchPlayers[i].id;
        players[id].all_scores[m] = matchPlayers[i].totalScore;
        players[id].putts[m] = totalPutts(i);
        players[id].matchPointsWon[m] = matchPlayers[i].totalPts;
        players[id].whichNinePlayed[m] = teamMatchHeader.whichNine;
        players[id].numHolesPlayed[m] = teamMatchHeader.nholesPlayed;

        // update a team member's sub history if a sub played for him
        if (matchPlayers[i].subFor != 0) {
            players[matchPlayers[i].subFor].subNumber[m] = matchPlayers[i].id;
            // remember which nine the team memeber was suposed to play
            // so the season summary program can re-create the schedule
            players[matchPlayers[i].subFor].whichNinePlayed[m] =
                teamMatchHeader.whichNine;
            //}

            // if this was a sub then award the points won to the team member the sub 
            // played for
            //if ( (matchPlayers[i].subFor != 0) && (matchPlayers[i].id != PAR_AS_SUB) )
            //{
            rP = matchPlayers[i].subFor;
            players[rP].matchPointsWon[m] = matchPlayers[i].totalPts;
            // remember which nine the team member was suposed to play
            // so the season summary program can re-create the schedule
            players[rP].whichNinePlayed[m] = teamMatchHeader.whichNine;
        }

    }                           // end of for loop
    return;
}                               // end of function upDatePlayersMatchInfo

void adjustForForfeits()
{
// if par is subbing because a player forfeits then we must substitute par's hcp 
// and enter par for each hole as the score
    int i;
    for (i = 0; i < 4; i++) {

        if (matchPlayers[i].id == PAR_AS_SUB) {
            matchPlayers[i].hcp = 0.0;
        }

    }                           // end for loop

    return;
}                               // end of function adjustForForfeits 

void computeMatchResults(char fname[], int nt, int np, FILE * db)
{
    FILE *fp;

    int i;
    int t1A, t1B, t2A, t2B;

    /* open the match file */
    if ((fp = fopen(fname, "r")) == NULL) {
        printf("Error:computeMatchResults-opening match file %s \n", fname);
        exit(-1);
    }

    readMatchHeader(fp);        // date, league match number,and number of team matches 

    /* loop over all team matches in the file and compute the results  */
    for (i = 0; i < matchFileHeader.numMatchesPlayed; i++) {

        initMatchStructure();
        readTeamMatch(fp, (i + 1), nt, np);
        setCourseSettings(teamMatchHeader.whichNine);
        getPlayerHCPs();
        getTotalScores();

        setABPlayers(t1A, t1B, t2A, t2B);   // hard coded for now
        adjustForForfeits();
        setHoleByHoleHcpStrokes(t1A, t2A);
        setHoleByHoleHcpStrokes(t1B, t2B);
        computeNetHoleScores();
        scoreMatch(t1A, t2A);
        scoreMatch(t1B, t2B);
        totalThePointsWon(t1A, t2A, t1B, t2B);
        writeDebugInfo(db, t1A, t1B, t2A, t2B);
        upDatePlayersMatchInfo();

    }                           // end of loop over the number of team matches played

    fclose(fp);

    return;
}                               // end of function readMatchFile

void rotateTheQue(int index)
{
    int i;

    for (i = 0; i < (HCP_WINDOW_LEN - 1); i++)
        players[index].ESCAdjustedScores[i] =
            players[index].ESCAdjustedScores[i + 1];

}                               // end of function rotateTheQue

int numberOfScores(int index)
{
    int i;
    int count = 0;

    for (i = 0; i < HCP_WINDOW_LEN; i++)
        if (players[index].ESCAdjustedScores[i] != NO_SCORE)
            count++;

    return (count);
}                               // end of function numberOfScores

/*
float totalHcpStrokes(int index)
{
    int i;
    float sum = 0.0;

    for (i = 0; i < HCP_WINDOW_LEN; i++)
        sum += (players[index].ESCAdjustedScores[i] - (float)TOTAL_PAR);

    return (sum);
}                               // end of function totalHcpScores
*/

int bestRnd(int index, int numScores)
{
    int i;
    int lowest;
    // if we loop from the begining of the vector we pick up the NO_SCORE value
    // which is a negative number and by default would be the lowest score
    lowest = (int)players[index].ESCAdjustedScores[HCP_WINDOW_LEN - 1]; // assume the most recent round is the best round
    for (i = HCP_WINDOW_LEN - 2; i >= 0; i--)
        if ((players[index].ESCAdjustedScores[i] < lowest) &&
            (players[index].ESCAdjustedScores[i] != NO_SCORE))
            lowest = (int)players[index].ESCAdjustedScores[i];

    return (lowest);
}

int worstRnd(int index, int numScores)
{
    int i;
    int highest;

    highest = (int)players[index].ESCAdjustedScores[HCP_WINDOW_LEN - 1];    // assume the most recent round worst round
    for (i = HCP_WINDOW_LEN - 2; i >= 0; i--)
        if ((players[index].ESCAdjustedScores[i] > highest) &&
            (players[index].ESCAdjustedScores[i] != NO_SCORE))
            highest = (int)players[index].ESCAdjustedScores[i];

    return (highest);
}

void upDateESCque(int limit)
{
    int mN;
    int i;

    mN = matchFileHeader.matchNumber - 1;   // make an index 

    for (i = FIRST_PLAYER; i <= limit; i++) {
        if (players[i].all_scores[mN] != NO_SCORE)  // only touch the records of those that played
            // I think this check is unnecessary. All
            // records would be updated correctly without it.
        {
            if (players[i].numHolesPlayed[mN] == 9) // if the player played all nine holes
            {
                // insert this round's score in the 8 round window used to compute hcps
                rotateTheQue(i);

                // add the new score to the last postion in the que
                players[i].ESCAdjustedScores[HCP_WINDOW_LEN -
                                             1] = players[i].ESCscore;

                //  remember the player's net score
                players[i].netScores[mN] =
                    players[i].all_scores[mN] - (int)players[i].curr_hcp;

                // compute the new hcp by averaging the hcp adjusted scores
                //players[i].curr_hcp = totalHcpStrokes( i ) / (float) HCP_WINDOW_LEN;

            }                   // end of if all nine holes played

        }
    }                           // end of for loop

    return;
}                               // end of function upDateHcps

void labelColumns(FILE * fp)    // debug routine to help line up the report output
{
    int i;

    for (i = 0; i < 8; i++)
        fprintf(fp, "0123456789");
    fprintf(fp, "\n");

    return;
}                               // end of function labelColumns

void writeTheFirstPageHeader(FILE * fp)
{
    fprintf(fp, "                         GDAIS Golf League \n\n");
    fprintf(fp, "         Results of Match # %2d, played on %s %2d,%4d\n\n",
            matchFileHeader.matchNumber,
            monthNames[matchFileHeader.month - 1],
            matchFileHeader.day, matchFileHeader.year);

    // labelColumns( fp );  
    fprintf(fp,
            "                                                  Points  Total   \n");
    fprintf(fp,
            "            Team#           Players                Won    Points  \n");
    fprintf(fp,
            "          ------------------------------------------------------  \n");

}                               // end of function writeTheFirstPageHeader

int sumOfPlayerPts(int index)
{
    int i;
    int sum = 0;

    for (i = 0; i < matchFileHeader.matchNumber; i++)
        if (players[index].matchPointsWon[i] != NO_PTS)
            sum += players[index].matchPointsWon[i];

    return (sum);
}                               // end of function sumOfPlayerPts

int addPlayersPts(int player1, int player2)
{
    int sum = 0;
    int mN = matchFileHeader.matchNumber - 1;   // convert to index
    int pts;

    pts = players[player1].matchPointsWon[mN];
    if (pts != NO_PTS)
        sum += pts;

    pts = players[player2].matchPointsWon[mN];
    if (pts != NO_PTS)
        sum += pts;

    return (sum);
}                               // end of function addPlayersPts

void getTeamInfo(int nteams)
{
    int i;
    int tN;

    for (i = 0; i < nteams; i++) {
        tN = i + 1;             // convert index to team number
        teamsInfo[i].teamNumber = tN;
        teamsInfo[i].teamCaptain = (2 * tN) - 1;
        teamsInfo[i].teamMate = 2 * tN;
        teamsInfo[i].teamPts =
            sumOfPlayerPts(teamsInfo[i].teamCaptain) +
            sumOfPlayerPts(teamsInfo[i].teamMate);
        teamsInfo[i].ptsWonThisMatch =
            addPlayersPts(teamsInfo[i].teamCaptain, teamsInfo[i].teamMate);

    }

}                               // end of function getTeamInfo

void sortTeamsByPts(int nteams) // Bubble sort - the only sort I could remember
{
    int done = false;
    int i;
    teamsInfo_t temp;

    while (!done) {
        done = true;
        for (i = 0; i < (nteams - 1); i++)
            if (teamsInfo[i].teamPts < teamsInfo[i + 1].teamPts) {
                temp.teamNumber = teamsInfo[i].teamNumber;
                temp.teamCaptain = teamsInfo[i].teamCaptain;
                temp.teamMate = teamsInfo[i].teamMate;
                temp.teamPts = teamsInfo[i].teamPts;
                temp.ptsWonThisMatch = teamsInfo[i].ptsWonThisMatch;

                teamsInfo[i].teamNumber = teamsInfo[i + 1].teamNumber;
                teamsInfo[i].teamCaptain = teamsInfo[i + 1].teamCaptain;
                teamsInfo[i].teamMate = teamsInfo[i + 1].teamMate;
                teamsInfo[i].teamPts = teamsInfo[i + 1].teamPts;
                teamsInfo[i].ptsWonThisMatch = teamsInfo[i + 1].ptsWonThisMatch;

                teamsInfo[i + 1].teamNumber = temp.teamNumber;
                teamsInfo[i + 1].teamCaptain = temp.teamCaptain;
                teamsInfo[i + 1].teamMate = temp.teamMate;
                teamsInfo[i + 1].teamPts = temp.teamPts;
                teamsInfo[i + 1].ptsWonThisMatch = temp.ptsWonThisMatch;

                // indicate a swap was made
                done = false;
            }
    }                           // end of while loop

    return;
}                               // end of function sortTeamsByPts

void writeTeamStandings(int limit, FILE * fp)
{
    int i;
    int max_name_len = findLongestName();

    for (i = 0; i < limit; i++)
        fprintf(fp,
                "            %2d     %1c %-*s - %1c %-*s    %2d    %4d \n",
                teamsInfo[i].teamNumber,
                players[teamsInfo[i].teamCaptain].initial, max_name_len,
                players[teamsInfo[i].teamCaptain].name,
                players[teamsInfo[i].teamMate].initial, max_name_len,
                players[teamsInfo[i].teamMate].name,
                teamsInfo[i].ptsWonThisMatch, teamsInfo[i].teamPts);

    fprintf(fp, "\n\n");

    return;
}                               // end of function writeTeamStandings

void writeFirstPage(FILE * fp, int nteams)
{

    writeTheFirstPageHeader(fp);
    getTeamInfo(nteams);
    sortTeamsByPts(nteams);
    writeTeamStandings(nteams, fp);

    return;
}                               // end of function writeFirstPage

void sortAllPlayersAlphabetically(int playerOrder[], int totalPlayers)
{
    int i;
    int done = false;
    int temp, n;

    // get the player order from the master database
    for (i = FIRST_PLAYER; i <= totalPlayers; i++)
        playerOrder[i] = players[i].id_number;

    // reorder the index vector so the players are in alphabetical order
    while (!done) {
        done = true;            // assume no swaps are performs
        for (i = FIRST_PLAYER; i <= totalPlayers - 1; i++) {
            n = strcmp(players[playerOrder[i]].name,
                       players[playerOrder[i + 1]].name);
            if (n > 0)          // names are not in alphabetical order
            {
                temp = playerOrder[i];
                playerOrder[i] = playerOrder[i + 1];
                playerOrder[i + 1] = temp;
                done = false;   // indicate an exchange occurred
            } else if (n == 0)  // the players last name are the same  -- check the first initials
                if (players[playerOrder[i]].initial >
                    players[playerOrder[i + 1]].initial) {
                    temp = playerOrder[i];
                    playerOrder[i] = playerOrder[i + 1];
                    playerOrder[i + 1] = temp;
                    done = false;   // indicate an exchange occurred
                }

        }                       // end of for loop

    }                           // end of the while loop

    return;
}                               // end of function sortAllPlayersAlphabetically

void writeTheSecondPageHeader(FILE * fp)
{

    /*  Remove page header to make room for more players 
       fprintf(fp,"               ERIM International Golf League -- 1997\n");
       fprintf(fp,"                     Individual Statistics   \n\n");
     */

    // labelColumns( fp );  

    fprintf(fp,
            "                        --------Points-----------  --Putts---   \n");

    fprintf(fp,
            "     Player    Hcp   Week  Total   Rnds  PerCt   Week  Avg  Score  ESC Score \n");

    return;
}                               // end of writeTheSecondPageHeader

int totalPlayersPtsForIndReport(int index)
{
    int i;
    int sum = 0;

    //  Total a players points for the weekly report - but don't add the points a sub won for the 
    //  player
    for (i = 0; i < MAX_RNDS; i++)
        if (players[index].all_scores[i] != NO_SCORE)
            sum += players[index].matchPointsWon[i];

    return (sum);
}                               // end of function totalPlayersPts

int countCompleteRoundsPlayed(int index)
{
    int i;
    int count = 0;

    // don't count rounds played by a sub
    for (i = 0; i < MAX_RNDS; i++)
        if ((players[index].all_scores[i] != NO_SCORE)
            && (players[index].numHolesPlayed[i] == 9))
            count++;

    return (count);
}                               // end of function totalRoundsPlayed

int countIncompleteRoundsPlayed(int index)
{
    int i;
    int count = 0;

    // don't count rounds played by a sub
    for (i = 0; i < MAX_RNDS; i++)
        if ((players[index].all_scores[i] != NO_SCORE)
            && (players[index].numHolesPlayed[i] < 9))
            count++;

    return (count);
}                               // end of function totalRoundsPlayed

float percentOfPointsWon(int totalRounds, int allPoints)
{
    float percent;

    percent =
        (float)((float)allPoints /
                ((float)totalRounds * (float)MAX_INDIVIDUAL_PTS) * 100.0);

    return (percent);
}                               // end of function percentOfPointsWon

float averagePuttsPerRound(int index, int nRounds)
{
    int i;
    float sum = 0.0;
    float ave;

    // this routine assumes (nRounds > 0) 

    for (i = 0; i < MAX_RNDS; i++)
        // if the player played this round and if it was a complete round then count the putts
        if ((players[index].putts[i] != NO_PUTTS)
            && (players[index].numHolesPlayed[i] == 9))
            sum += players[index].putts[i];

    if (sum > 0.0) {
        ave = sum / (float)nRounds;
    } else {
        ave = 0.0;
    }

    return (ave);
}                               // end of fuction averagePuttsPerRound

void writePlayerInfo(FILE * fp, int totalPlayers, int playerOrder[])
{
    int i;
    int n;
    int allPoints, numCompleteRnds, numIncompleteRnds, totalRnds;
    float percentage;
    float aveputts;

    int maxNameLen = findLongestName();
    int matchIndex = matchFileHeader.matchNumber - 1;   // convert to index

    for (i = FIRST_PLAYER; i <= totalPlayers; i++) {
        n = playerOrder[i];
        fprintf(fp, "  %-*s %1c ", maxNameLen, players[n].name,
                players[n].initial);

        if (players[n].curr_hcp == NO_HCP)  // no hcp - print 4 blanks
            fprintf(fp, "    ");
        else
            fprintf(fp, "  %4.1f ", players[n].curr_hcp);

        allPoints = totalPlayersPtsForIndReport(n);

        numCompleteRnds = countCompleteRoundsPlayed(n);
        numIncompleteRnds = countIncompleteRoundsPlayed(n);
        totalRnds = numCompleteRnds + numIncompleteRnds;

        if (totalRnds != 0)
            percentage = percentOfPointsWon(totalRnds, allPoints);

        if ((numCompleteRnds == 0) && (players[n].subNumber[matchIndex] == NO_SUB)) // players has not played any complete rounds
        {
            fprintf(fp, "\n");
            continue;
        }

        if (players[n].subNumber[matchIndex] != NO_SUB) // Player either had a sub or forfeited and par was the sub
        {
            fprintf(fp, "   %2d ", players[n].matchPointsWon[matchIndex]);
        } else if (players[n].all_scores[matchIndex] == NO_SCORE)   // no points this week
        {
            fprintf(fp, "         %3d     %2d     %4.1f ",
                    allPoints, totalRnds, percentage);
        } else if (players[n].subNumber[matchIndex] != NO_SUB)  // Player either had a sub or forfeited and par was the sub
        {
            fprintf(fp, "   %2d ", players[n].matchPointsWon[matchIndex]);
        } else                  // this player played the round so show his points won 
        {
            fprintf(fp, "   %2d    %3d     %2d     %4.1f ",
                    players[n].matchPointsWon[matchIndex],
                    allPoints, totalRnds, percentage);
        }

        aveputts = averagePuttsPerRound(n, numCompleteRnds);

        if ((players[n].all_scores[matchIndex] == NO_SCORE) && (aveputts > 0.0))    // didn't play, just show averageputts if he has any
            fprintf(fp, "       %4.1f  ", aveputts);
        else if (aveputts > 0.0)    // this player played a complete round
        {
            fprintf(fp, "  %2d   %4.1f  ",
                    players[n].putts[matchIndex], aveputts);
            fprintf(fp, "  %2d  %4.1f",
                    players[n].all_scores[matchIndex], players[n].ESCscore);
        }

        fprintf(fp, "\n");

    }

}                               // end of function writePlayerInfo

void writeSecondPage(FILE * fp, int totalPlayers)
{
    int playerOrder[MAX_PLAYERS];

    sortAllPlayersAlphabetically(playerOrder, totalPlayers);

    writeTheSecondPageHeader(fp);

    writePlayerInfo(fp, totalPlayers, playerOrder);

    return;
}                               // end of function writeSecondPage

void writeReport(int nteams, char fileName[], int totalPlayers)
{
    FILE *fp;

    if ((fp = fopen(fileName, "w")) == NULL) {
        printf("Error:writeReport-opening match results file \n");
        exit(-1);
    }
    // write the team information
    writeFirstPage(fp, nteams);

    // write the individual players report
    writeSecondPage(fp, totalPlayers);

}                               // end of function writeReport

void findSeasonNetLowScore(int nplayers)
{
    int i, m;

    int netlow = 100;

    // the lowest recorded net score
    for (i = FIRST_PLAYER; i <= nplayers; i++)
        for (m = 0; m < matchFileHeader.matchNumber; m++)
            if (players[i].numHolesPlayed[m] == 9)  // only include full rnds
                if ((players[i].netScores[m] <
                     netlow) & (players[i].netScores[m] != NO_NET_SCORE))
                    netlow = players[i].netScores[m];

    printf("Season net low is %d \n", netlow);

    // now that the lowest net low score is known--find all players that have matched it
    for (i = FIRST_PLAYER; i <= nplayers; i++)
        for (m = 0; m < matchFileHeader.matchNumber; m++)
            if ((players[i].netScores[m] ==
                 netlow) & (players[i].numHolesPlayed[m] == 9))
                printf("  %s match number %d \n", players[i].name, (m + 1));

}

int main(int argc, char *argv[])
{
    int nTeams = 0;
    int nPlayerInDatabase = 0;
    FILE *db;

    // printf(" argc = %d \n",argc);
    // for ( i = 0; i < argc; i++) printf("argv %d = %s \n",i,argv[i]);

    /* test structures to debug code */

    /*
       char match_file_name[MAX_FILENAME_LEN]   = "c:\\Documents and Settings\\Tom Wessling\\Golf2003\\DadGolf\\03match3.txt";
       char master_file_name[MAX_FILENAME_LEN]  = "c:\\Documents and Settings\\Tom Wessling\\Golf2003\\DadGolf\\master2";
       char newMasterFileName[MAX_FILENAME_LEN] = "c:\\Documents and Settings\\Tom Wessling\\Golf2003\\DadGolf\\master3";  
       char reportFileName[MAX_FILENAME_LEN]    = "c:\\Documents and Settings\\Tom Wessling\\Golf2003\\DadGolf\\rep3";
     */

    char debugFile[MAX_FILENAME_LEN] = "db.dat";

    char *match_file_name;
    char *master_file_name;
    char *newMasterFileName;
    char *reportFileName;

    if (argc != 5) {
        printf
            ("ERROR:Score: Not the correct number of command line arguments \n");
        exit(-1);
    } else                      // assign file names from command line to pointers
    {
        match_file_name = argv[1];
        master_file_name = argv[2];
        newMasterFileName = argv[3];
        reportFileName = argv[4];
    }

    /* open a file for debugging information */
    if ((db = fopen(debugFile, "w")) == NULL) {
        printf("Error:main-opening debug file %s \n", debugFile);
        exit(-1);
    }
    // read the master file 
    readMasterFile(master_file_name, nTeams, nPlayerInDatabase);

    // compute the results of the match and update master file 
    computeMatchResults(match_file_name, nTeams, nPlayerInDatabase, db);

    // compute new hcps and update the master records 
    upDateESCque(nPlayerInDatabase);

    // recompute hcps for all players
    compute_hcps(nPlayerInDatabase);

    // write the match results report 
    writeReport(nTeams, reportFileName, nPlayerInDatabase);

    // write the updated master file data to disk 
    write_master_file(newMasterFileName, nTeams, nPlayerInDatabase);

    fclose(db);

    findSeasonNetLowScore(nPlayerInDatabase);

    return (0);

}                               /* end of main program  */
